Amazon Redshift [新機能]マテリアライズドビューの徹底解説
昨年のre:Invent2019で発表されたAmazon Redshiftのマテリアライズドビューがクラスタバージョン1.0.13448からGA(Generally Available:正式リリース)になりました。マイグレーションの際にご要望の多かったマテリアライズドビューが本番リリースしましたので早速試してみました。
目次
- マテリアライズドビューとは
- マテリアライズドビューの基本操作
- 作成
- 問い合わせ
- リフレッシュ
- 削除
- マテリアライズドビューの強み「増分リフレッシュ」
- マテリアライズドビューの最適化
- マテリアライズドビューの監視
- 増分リフレッシュの検証
- 検証シナリオ
- マテリアライズドビュー
- 年度ごとのデータの追加とマテリアライズドビューのリフレッシュ
- マテリアライズドビューの情報
- マテリアライズドビューを一括で作成した時間の計測
- 検証結果
- 内部的な動きの確認
- マテリアライズドビュー利用のベストプラクティス
- 最後に
マテリアライズドビューとは
通常のビューはクエリを実行するたびにビュー定義内のクエリ(結合、フィルタ、集計)も繰り返し実行されます。一方、マテリアライズドビューはビュー定義内のクエリ(結合、フィルタ、集計)の実行結果を保持することで、ビュー定義内のクエリの実行することなく結果が得られるため、CPUやIOの負荷を抑え、素早く結果が得られるようになります。通常のビューはマクロで、マテリアライズドビューはテーブルとイメージに近いです。Redshiftのマテリアライズドビューは、新規作成した時に実行結果が保持され、これ以降はリフレッシュコマンドを実行することでデータが更新されます。
通常のビューとマテリアライズドビューの違いは以下のとおりです。
View | Materialized View | |
---|---|---|
ユースケース | 複雑なクエリをビューにまとめたり、レコードやカラムの参照を制限する | 繰り返し同じテーブルを参照するクエリのパフォーマンス改善 |
動作 | ビュー内SQLを常に実行して結果を返す | 保持しているSQLの実行結果を返す |
データ更新 | 不要(常に最新の結果が得られる) | 必要(リフレッシュが必要、増分データのみ更新、更新したいタイミングでコマンドを実行する) |
応答速度 | 遅い | 速い |
ユーザーの観点から見て、クエリの結果は、ソーステーブルから同じデータを取得に要する時間に比べて短時間で返ってきます。
マテリアライズドビューの基本操作
作成
マテリアライズドビューは、通常のビューと同じ様に作成可能です。追加のオブションについては後ほど解説します。
cmdb=> CREATE MATERIALIZED VIEW tickets_mv AS cmdb-> select catgroup, cmdb-> sum(qtysold) as sold cmdb-> from category c, event e, sales s cmdb-> where c.catid = e.catid cmdb-> and e.eventid = s.eventid cmdb-> group by catgroup; CREATE MATERIALIZED VIEW Time: 1128.051 ms (00:01.128)
補足:psqlからオブジェクトを参照すると、マテリアライズドビューの実態は、rdsdbユーザーが所有するtickets_mvテーブルとオブジェクトオーナーが作成したmv_tbl__tickets_mv__0ビューを組み合わせです。マテリアライズドビューはこの組み合わせで管理されます。
cmdb=> \d schema | name | type | owner --------+-----------------------+-------+--------- tickit | category | table | cm_user tickit | date | table | cm_user tickit | event | table | cm_user tickit | listing | table | cm_user tickit | mv_tbl__tickets_mv__0 | table | rdsdb tickit | sales | table | cm_user tickit | tickets_mv | view | cm_user tickit | users | table | cm_user tickit | venue | table | cm_user (9 rows) cmdb=> \d tickets_mv View "tickit.tickets_mv" Column | Type | Collation | Nullable | Default ----------+-----------------------+-----------+----------+--------- catgroup | character varying(10) | | | sold | bigint | | |
問い合わせ
通常のビューと同じ様に問い合わせできます。
cmdb=> SELECT count(*) FROM tickets_mv; count ------- 2 (1 row) Time: 232.459 ms cmdb=> SELECT * FROM tickets_mv; catgroup | sold ----------+-------- Concerts | 195444 Shows | 149905 (2 rows) Time: 249.984 ms
リフレッシュ
ソーステーブルは更新していませんがREFRESH MATERIALIZED VIEW
コマンドを実行してみました。最近コミットされた更新を除いて更新されましたといったメッセージが出力されれています。
cmdb=> REFRESH MATERIALIZED VIEW tickets_mv; INFO: Materialized view tickets_mv was updated excluding some recently committed updates. Please run REFRESH again later. REFRESH Time: 530.498 ms
削除
DROP VIEW
ではなく、DROP MATERIALIZED VIEW
で削除します。
cmdb=> DROP VIEW tickets_mv; ERROR: "tickets_mv" is not a view HINT: Use DROP MATERIALIZED VIEW to remove a materialized view. Time: 332.463 ms cmdb=> DROP MATERIALIZED VIEW tickets_mv; DROP MATERIALIZED VIEW Time: 413.982 ms
最低限、マテリアライズドビューを利用したいのであればここまでで十分かもしれません。データマートの更新とどう違うのか、内部的のどの様に実現されているかを把握したい場合は以降を御覧ください。
マテリアライズドビューの強み「増分リフレッシュ」
クエリの実行結果をテーブルに保存して再利用するならデータマートや1次集計テーブルで十分です。マテリアライズドビューでは、ビューを構成するソーステーブルに加えられた増分データを対象にデータ更新する「増分リフレッシュ」するので更新負荷が少なく、短時間で済みます。
REFRESH MATERIALIZED VIEW
を実行すると、「増分リフレッシュ」を試み、インクリメンタルに更新できない場合は「フルリフレッシュ」によって更新します。「増分リフレッシュ」が可能であるかは、STV_MV_INFOビューのstate(マテリアライズドビューの状態)から確認できます。
マテリアライズドビューの最適化
マテリアライズドビューは「テーブルとイメージに近い」と解説しましたが、実態はテーブルなので分散キーとソートキーを指定してテーブルを最適化し、クエリのパフォーマンスを改善できます。2〜4行目に追加しています。
cmdb=> CREATE MATERIALIZED VIEW tickets_mv DISTSTYLE KEY DISTKEY(catgroup) SORTKEY(catgroup) AS ( select catgroup, sum(qtysold) as sold from category c, event e, sales s where c.catid = e.catid and e.eventid = s.eventid group by catgroup ) ; CREATE MATERIALIZED VIEW Time: 1136.064 ms (00:01.136)
マテリアライズドビューの監視
マテリアライズドビューを監視するビューが追加されています。
- STV_MV_INFO:すべてのマテリアライズドビューの行、データが古くなっているかどうか、およびステータス情報が含まれる
- STL_MV_STATE:マテリアライズドビューのすべてのステータス遷移の行が含まれる
- SVL_MV_REFRESH_STATUS:マテリアライズドビューの更新作業用の行が含まれる
上記のビューをそれぞれ実行した結果です。STV_MV_INFOビューのis_staleが
cmdb=> select * from STV_MV_INFO; -[ RECORD 1 ]----+---------------------------------------------------------------------- db_name | cmdb schema | tickit name | tickets_mv updated_upto_xid | 44998616 is_stale | t owner_user_name | state | 1 Time: 255.584 ms cmawsteamdb=> select * from STL_MV_STATE; -[ RECORD 1 ]----+---------------------------------------------------------------------- userid | 111 starttime | 2020-03-06 08:17:19.199417 xid | 44998647 event_desc | Vacuum db_name | cmdb base_table_schema | tickit base_table_name | category mv_schema | tickit mv_name | tickets_mv state | Recompute Time: 236.227 ms cmdb=> select * from SVL_MV_REFRESH_STATUS; -[ RECORD 1 ]----+---------------------------------------------------------------------- db_name | cmdb userid | 111 schema_name | tickit mv_name | tickets_mv xid | 44998617 starttime | 2020-03-06 08:10:47.425842 endtime | 2020-03-06 08:11:00.511995 status | Refresh partially updated MV incrementally up to an active transaction Time: 260.425 ms
増分リフレッシュの検証
検証シナリオ
マテリアライズドビューで最も気にな機能は、「増分リフレッシュ」です。追加したデータにのみ集計・追加が実行され、マテリアライズドビューのリフレッシュ時間が単調増加しないことを確認します。今回は、顧客ごとの1992年から1998年までの注文額(c_orderyaer)を集計するマテリアライズドビューを例に機能を検証します。1992年、1993年、1994年、、、1998年と年毎にデータ(lineorder_all)を追加しながらマテリアライズドビューをリフレッシュします。
ファクトがlineorder、ディメンジョンがcustomer、データの分布や件数は以下のとおりです。
cmdb=# select count(*) from lineorder_all; count ----------- 600037902 (1 row) cmdb=# select trunc(lo_orderdate,-4) as c_orderyear, count(*) as cnt from lineorder_all group by 1 order by 1; c_orderyear | cnt -------------+---------- 19920000 | 91248844 19930000 | 91007488 19940000 | 91044214 19950000 | 91016436 19960000 | 91301792 19970000 | 91050840 19980000 | 53368288 (7 rows) cmdb=# select count(*) from customer; count --------- 3000000 (1 row)
マテリアライズドビュー
顧客ごとの1992年から1998年までの注文額(c_orderyaer)を集計するマテリアライズドビューです。ファクト(lineorder)とディメンジョン(customer)を結合して、顧客と年度で注文額(c_orderyaer)を集計します。下記の通り、集計クエリを書いているだけで、差分をどうするなど何も定義する必要はありません。
-- DROP MATERIALIZED VIEW sales_customer_mv; CREATE MATERIALIZED VIEW sales_customer_mv DISTSTYLE KEY DISTKEY(c_custkey) SORTKEY(c_custkey, c_name, c_orderyear) AS ( SELECT c_custkey, c_name, trunc(lo_orderdate,-4) as c_orderyear, sum(lo_ordertotalprice) as c_ordertotal FROM lineorder l INNER JOIN customer c ON l.lo_custkey = c.c_custkey GROUP BY 1,2,3 ) ;
年度ごとのデータの追加とマテリアライズドビューのリフレッシュ
マテリアライズドビューの作成
cmdb=> DROP MATERIALIZED VIEW sales_customer_mv; DROP MATERIALIZED VIEW Time: 385.378 ms cmdb=> CREATE MATERIALIZED VIEW sales_customer_mv cmdb-> DISTSTYLE KEY cmdb-> DISTKEY(c_custkey) cmdb-> SORTKEY(c_custkey, c_name, c_orderyear) cmdb-> AS ( cmdb(> SELECT cmdb(> c_custkey, cmdb(> c_name, cmdb(> trunc(lo_orderdate,-4) as c_orderyear, cmdb(> sum(lo_ordertotalprice) as c_ordertotal cmdb(> FROM lineorder l cmdb(> INNER JOIN customer c ON l.lo_custkey = c.c_custkey cmdb(> GROUP BY 1,2,3 cmdb(> ) cmdb-> ; CREATE MATERIALIZED VIEW Time: 944.159 ms cmdb=> select count(*) from sales_customer_mv; count ------- 0 (1 row) Time: 223.625 ms
1992年のデータの追加とリフレッシュ
cmdb=> INSERT INTO lineorder SELECT * FROM lineorder_all WHERE lo_orderdate > 19920000 AND lo_orderdate < 19930000; INSERT 0 91248844 cmdb=> REFRESH MATERIALIZED VIEW sales_customer_mv; INFO: Table "mv_tbl__sales_customer_mv__0_tmp" does not exist and will be skipped INFO: Table "mv_tbl__sales_customer_mv__0__insert_tmp" does not exist and will be skipped INFO: Materialized view sales_customer_mv was incrementally updated successfully. REFRESH Time: 61010.016 ms (01:01.010) cmdb=> select count(*) from sales_customer_mv; count --------- 1999625 (1 row) Time: 214.519 ms
1993年のデータの追加とリフレッシュ
cmdb=> INSERT INTO lineorder SELECT * FROM lineorder_all WHERE lo_orderdate > 19930000 AND lo_orderdate < 19940000; INSERT 0 91007488 cmdb=> REFRESH MATERIALIZED VIEW sales_customer_mv; INFO: Materialized view sales_customer_mv was incrementally updated successfully. REFRESH Time: 32679.170 ms (00:32.679) cmdb=> select count(*) from sales_customer_mv; count --------- 3999239 (1 row) Time: 230.856 ms
1994年のデータの追加とリフレッシュ
cmdb=> INSERT INTO lineorder SELECT * FROM lineorder_all WHERE lo_orderdate > 19940000 AND lo_orderdate < 19950000; INSERT 0 91044214 cmdb=> REFRESH MATERIALIZED VIEW sales_customer_mv; INFO: Materialized view sales_customer_mv was incrementally updated successfully. REFRESH Time: 47750.169 ms (00:47.750) cmdb=> select count(*) from sales_customer_mv; count --------- 5998849 (1 row) Time: 274.657 ms
1995年のデータの追加とリフレッシュ
cmdb=> INSERT INTO lineorder SELECT * FROM lineorder_all WHERE lo_orderdate > 19950000 AND lo_orderdate < 19960000; INSERT 0 91016436 cmdb=> REFRESH MATERIALIZED VIEW sales_customer_mv; INFO: Materialized view sales_customer_mv was incrementally updated successfully. REFRESH Time: 26067.248 ms (00:26.067) cmdb=> select count(*) from sales_customer_mv; count --------- 7998447 (1 row) Time: 242.446 ms
1996年のデータの追加とリフレッシュ
cmdb=> INSERT INTO lineorder SELECT * FROM lineorder_all WHERE lo_orderdate > 19960000 AND lo_orderdate < 19970000; INSERT 0 91301792 cmdb=> REFRESH MATERIALIZED VIEW sales_customer_mv; INFO: Materialized view sales_customer_mv was incrementally updated successfully. REFRESH Time: 22812.124 ms (00:22.812) cmdb=> select count(*) from sales_customer_mv; count --------- 9998075 (1 row) Time: 240.592 ms
1997年のデータの追加とリフレッシュ
cmdb=> INSERT INTO lineorder SELECT * FROM lineorder_all WHERE lo_orderdate > 19970000 AND lo_orderdate < 19980000; INSERT 0 91050840 cmdb=> REFRESH MATERIALIZED VIEW sales_customer_mv; INFO: Materialized view sales_customer_mv was incrementally updated successfully. REFRESH Time: 23197.663 ms (00:23.198) cmdb=> select count(*) from sales_customer_mv; count ---------- 11997683 (1 row) Time: 260.385 ms
1998年のデータの追加とリフレッシュ
cmdb=> INSERT INTO lineorder SELECT * FROM lineorder_all WHERE lo_orderdate > 19980000 AND lo_orderdate < 19990000; INSERT 0 53368288 cmdb=> REFRESH MATERIALIZED VIEW sales_customer_mv; INFO: Materialized view sales_customer_mv was incrementally updated successfully. REFRESH Time: 18362.876 ms (00:18.363) cmdb=> select count(*) from sales_customer_mv; count ---------- 13987587 (1 row) Time: 251.285 ms
上記クエリのメトリクス
ソースデータの追加と増分リフレッシュを交互に実行したことが影響しているかもしれませんが、想定以上にCPUの利用が高い印象です。
マテリアライズドビューの情報
リフレッシュ実行後の状態を確認します。STV_MV_INFOは、マテリアライズドビューの行、データが古くなっているかどうか、およびステータス情報が含まれます。以下の結果から「増分リフレッシュ」であることが確認できます。
- is_stale:
f
(最新状態) - state:
1
(マテリアライズドビューは増分)
cmdb=# select * from STV_MV_INFO; -[ RECORD 1 ]----+------------------------------------------------------------------------ db_name | cmdb schema | cm_user name | sales_customer_mv updated_upto_xid | 45002987 is_stale | f owner_user_name | state | 1
マテリアライズドビューを一括で作成した時間の計測
増分リフレッシュ時間と比較するため、マテリアライズドビューを一括で作成した時間を計測します。
cmdb=> CREATE MATERIALIZED VIEW sales_customer_all_mv cmdb-> DISTSTYLE KEY cmdb-> DISTKEY(c_custkey) cmdb-> SORTKEY(c_custkey, c_name, c_orderyear) cmdb-> AS ( cmdb(> SELECT cmdb(> c_custkey, cmdb(> c_name, cmdb(> trunc(lo_orderdate,-4) as c_orderyear, cmdb(> sum(lo_ordertotalprice) as c_ordertotal cmdb(> FROM lineorder l cmdb(> INNER JOIN customer c ON l.lo_custkey = c.c_custkey cmdb(> GROUP BY 1,2,3 cmdb(> ) cmdb-> ; CREATE MATERIALIZED VIEW Time: 126897.630 ms (02:06.898)
検証結果
初回(1992年)と最後(1998年)の増分リフレッシュを除くと約30秒程度でした。マテリアライズドビューを一括で作成した時間が126秒でしたので、増分リフレッシュのほうが累計では時間がかかっていますが、明らかにフルリフフレッシュよりも処理時間が短縮できたことが確認できました。
| | ファクト(lineorder)の 追加レコード数 | マテリアライズドビューの 追加レコード数 | 増分リフレッシュ時間(秒) | | ---------- | ----------------------------------------- | ------------------------------------------ | ------------------------------ | | 1992年 | 91248844 | 1999625 | 61.010 | | 1993年 | 91007488 | 1999614 | 32.679 | | 1994年 | 91044214 | 1999610 | 47.750 | | 1995年 | 91016436 | 1999598 | 26.067 | | 1996年 | 91301792 | 1999628 | 22.812 | | 1997年 | 91050840 | 1999628 | 23.197 | | 1998年 | 53368288 | 1989904 | 18.362 |
内部的な動きの確認
では、どのように増分リフレッシュが実現されているのか気になるところです。クエリ履歴からリフレッシュコマンド REFRESH MATERIALIZED VIEW
を実行すると、内部的には以下の2つクエリが順に実行されることが確認できました。insertxid
やdeletexid
などの更新情報を利用して、増分リフレッシュを実現していることが推測されます。一方、汎用的な仕組みであるため、私がデータマートの更新に用いている更新クエリと比較して複雑になっており、それがCPUやIOの上昇の要因の一つであると考えられます。
CREATE TEMPORARY TABLE "mv_tbl__sales_customer_mv__0_tmp" AS( SELECT "derived_table1"."X1" AS "grvar_1", "derived_table1"."X2" AS "grvar_2", TRUNC("derived_table1"."X3", CAST(- 4 AS INT4)) AS "grvar_3", SUM(("derived_table1"."X4" * "derived_table1"."X5")) AS "aggvar_1", SUM("derived_table1"."X5") AS "num_rec" FROM (( SELECT "c"."c_custkey" AS "X1", "c"."c_name" AS "X2", "l"."lo_orderdate" AS "X3", "l"."lo_ordertotalprice" AS "X4", (CAST(1 AS INT4) * CAST(1 AS INT4)) AS "X5", "l"."lo_custkey" AS "X6" FROM "cm_user"."customer" AS "c", "cm_user"."lineorder" AS "l" WHERE ( ( CAST("c"."insertxid" AS INT8) > $1 ) AND ( ( CAST("c"."insertxid" AS INT8) <= $2 ) AND ( CAST("c"."deletexid" AS INT8) > $3 ) ) ) AND ( ( CAST("l"."insertxid" AS INT8) <= $4 ) AND ( CAST("l"."deletexid" AS INT8) > $5 ) ) ) UNION ALL(( SELECT "c"."c_custkey" AS "X1", "c"."c_name" AS "X2", "l"."lo_orderdate" AS "X3", "l"."lo_ordertotalprice" AS "X4", (CAST(1 AS INT4) * CAST(1 AS INT4)) AS "X5", "l"."lo_custkey" AS "X6" FROM "cm_user"."customer" AS "c", "cm_user"."lineorder" AS "l" WHERE ( ( CAST("c"."insertxid" AS INT8) <= $6 ) AND ( CAST("c"."deletexid" AS INT8) > $7 ) ) AND ( ( CAST("l"."insertxid" AS INT8) > $8 ) AND ( ( CAST("l"."insertxid" AS INT8) <= $9 ) AND ( CAST("l"."deletexid" AS INT8) > $10 ) ) ) ) UNION ALL(( SELECT "c"."c_custkey" AS "X1", "c"."c_name" AS "X2", "l"."lo_orderdate" AS "X3", "l"."lo_ordertotalprice" AS "X4", (CAST(- 1 AS INT4) * CAST(1 AS INT4)) AS "X5", "l"."lo_custkey" AS "X6" FROM "cm_user"."customer" AS "c", "cm_user"."lineorder" AS "l" WHERE ( ( CAST("c"."insertxid" AS INT8) <= $11 ) AND ( ( CAST("c"."deletexid" AS INT8) > $12 ) AND ( CAST("c"."deletexid" AS INT8) <= $13 ) ) ) AND ( ( CAST("l"."insertxid" AS INT8) <= $14 ) AND ( CAST("l"."deletexid" AS INT8) > $15 ) ) ) UNION ALL( SELECT "c"."c_custkey" AS "X1", "c"."c_name" AS "X2", "l"."lo_orderdate" AS "X3", "l"."lo_ordertotalprice" AS "X4", (CAST(1 AS INT4) * CAST(- 1 AS INT4)) AS "X5", "l"."lo_custkey" AS "X6" FROM "cm_user"."customer" AS "c", "cm_user"."lineorder" AS "l" WHERE ( ( CAST("c"."insertxid" AS INT8) <= $16 ) AND ( CAST("c"."deletexid" AS INT8) > $17 ) ) AND ( ( CAST("l"."insertxid" AS INT8) <= $18 ) AND ( ( CAST("l"."deletexid" AS INT8) > $19 ) AND ( CAST("l"."deletexid" AS INT8) <= $20 ) ) ) )))) AS "derived_table1" WHERE "derived_table1"."X1" = "derived_table1"."X6" GROUP BY "derived_table1"."X1", "derived_table1"."X2", TRUNC("derived_table1"."X3", CAST(- 4 AS INT4)) ) ``` ```sql CREATE TEMPORARY TABLE "mv_tbl__sales_customer_mv__0__insert_tmp" AS (SELECT "mv_tbl__sales_customer_mv__0_tmp"."grvar_1", "mv_tbl__sales_customer_mv__0_tmp"."grvar_2", "mv_tbl__sales_customer_mv__0_tmp"."grvar_3", CASE WHEN "mv_tbl__sales_customer_mv__0"."aggvar_1" IS NULL THEN "mv_tbl__sales_customer_mv__0_tmp"."aggvar_1" ELSE "mv_tbl__sales_customer_mv__0"."aggvar_1" + "mv_tbl__sales_customer_mv__0_tmp"."aggvar_1" END AS "aggvar_1", CASE WHEN "mv_tbl__sales_customer_mv__0".num_rec IS NULL THEN "mv_tbl__sales_customer_mv__0_tmp".num_rec ELSE "mv_tbl__sales_customer_mv__0".num_rec + "mv_tbl__sales_customer_mv__0_tmp".num_rec END AS num_rec FROM "mv_tbl__sales_customer_mv__0_tmp" LEFT OUTER JOIN "cm_user"."mv_tbl__sales_customer_mv__0" ON "mv_tbl__sales_customer_mv__0"."grvar_1" = "mv_tbl__sales_customer_mv__0_tmp"."grvar_1" AND "mv_tbl__sales_customer_mv__0"."grvar_2" = "mv_tbl__sales_customer_mv__0_tmp"."grvar_2" AND "mv_tbl__sales_customer_mv__0"."grvar_3" = "mv_tbl__sales_customer_mv__0_tmp"."grvar_3" WHERE "mv_tbl__sales_customer_mv__0_tmp".num_rec + NVL("mv_tbl__sales_customer_mv__0".num_rec, 0) > 0)
psqlからオブジェクトを参照するとsales_customer_mvというビューとmv_tbl__sales_customer_mv__0テーブルが追加されています。最終的にmv_tbl__sales_customer_mv__0を更新するクエリは確認できませんが、内部的に実行されているのではないかと考えられます。
cmdb=> \d List of relations schema | name | type | owner ---------+----------------------------------+-------+--------- cm_user | mv_tbl__sales_customer_mv__0 | table | rdsdb cm_user | sales_customer_mv | view | cm_user
マテリアライズドビュー利用のベストプラクティス
複雑なクエリをビューにまとめたり、レコードやカラムの参照を制限が必要な場合は、まずは通常のビューで作成します。次に参照頻度が高いビューやパフォーマンスの改善が必要な場合はマテリアライズドビューに置き換えます。更に関連するソーステーブルや対象レコードが多く、テーブル間の仕様を把握して更新クエリの最適化による効率的なメンテナンスが可能な場合は、データマートの置き換えを検討すると良いでしょう。
これまでマテリアライズドビューのワークアラウンドとして利用してきたデータマートは、テーブルの仕様を把握して更新系クエリを組み合わせてUPSERTしていますので、手間がかかる一方で最適化による効率的なメンテナンスができるというメリットもあります。以下の条件に従い、これらを選択することをおすすめします。
- 通常のビュー
- 複雑なクエリやビジネスロジックを集約したい
- レコードやカラムのアクセスコントロール
- マテリアライズドビュー(通常のビューを置き換える)
- ソーステーブルの更新頻度が低い
- スループットやレスポンスの改善が求められる
- 参照頻度が高い通常のビューの置き換え
- データマート(マテリアライズドビューを置き換える)
- 関連するソーステーブルや対象レコードが多い
- テーブル間の仕様を把握して更新クエリの最適化による効率的なメンテナンス
最後に
Redshiftのマテリアライズドビューは、通常のビューと同じ様に作成することが可能であり、任意のタイミングでリフレッシュを実行することで更新できます。リフレッシュを実行すると、増分リフレッシュを試み、インクリメンタルに更新できない場合はフルリフレッシュされます。マテリアライズドビューは、Redshiftのテーブルと同様に分散キーやソートキーによる最適化が可能です。増分リフレッシュは、テーブル内部のinsertxid
やdeletexid
などの更新情報を用いて実現していますので、ユーザーはマテリアライズドビューをリフレッシュするだけで簡単に更新できます。
通常のビュー、マテリアライズドビュー、データマートの使い分けについては、「マテリアライズドビュー利用のベストプラクティス」を参考にしていただけると幸いです。
合わせて読みたい
フルリフレッシュではなく、増分リフレッシュになる条件については以下のブログを御覧ください。
Amazon Redshift: マテリアライズド・ビュー(Materialized View)のリフレッシュ(REFRESH)について